package logic.gridNodes;

import eduni.simjava.Sim_entity;
import gridsim.net.FIFOScheduler;
import gridsim.net.RIPRouter;
import gridsim.net.SimpleLink;
import java.util.LinkedList;
import java.util.Vector;
import logic.gridSimInterface.brokers.BrokerLocal;
import logic.topology.NetworkLink;
import logic.topology.NetworkNode;

/**
 * Broker moze byt spojeny iba s jednym globalnym a s jednym lokalnym routerom
 */
public class GridBroker extends NetworkNode {
    
    private static int count = 0;

    private static final String _obmedzenie =
            "Broker must be connected to one global and one local router!";
    
    public GridBroker() throws Exception {
        super("GridBrokerDefault123456789xtea",0,0);
        count++;
        setName("Broker" + count);
    }

    /**
     * <font face="">vracia null, ak je mozne vytvorit novu linku z daneho uzla
     * (este 
     *    sa nevie, aky bude koniec linky). Inak vracia dovod, preco sa nemoze 
     *    vytvorit
     * </font>
     */
    public String canCreateNewLink() {
        if (getLinks().size() < 2)
            return null;
        else
            return new String(_obmedzenie);
    }

    /**
     * Vracia null, ak moze vytvorit spojenie s danym elementom. Inak vracia
     * dovod, 
     *    preco nie. Kontroluje aj, ci uz linka s danym elementom neexistuje.
     */
    public String canCreateNewLink(NetworkNode end) {
        if (getLinks().size() >= 2)
            return new String(_obmedzenie);

        if (!(end instanceof GridRouterGlobal) && !(end instanceof GridRouterLocal))
            return new String(_obmedzenie);
        
        if (getLinks().size() == 0) {
            //ide sa vytvorit prve spojenie
            return null;
        } else {
            //jedna linka uz existuje, ide sa urobit druha
            NetworkNode firstRouter = (NetworkNode) (getLinks().iterator().next().getAnotherEnd(this));
            if ((firstRouter instanceof GridRouterGlobal) && (end instanceof GridRouterLocal))
                return null;
            if ((firstRouter instanceof GridRouterLocal) && (end instanceof GridRouterGlobal))
                return null;
        }
        
        return new String(_obmedzenie);
    }
    
    /**
     * Vracia zoznam vsetkych lokanych entit patriacich danemu brokerovi.
     * Tj. vsetky entity, ktore su ku nemu pripojene cez lokalny router.
     * Ak je topologia spravna, mali by to byt iba jeho lokalne routre,
     * lokalne zdroje a uzivatela. Ak je cez lokalny router napojeny na ineho
     * brokera, vracia aj jeho.
     */
    public Vector<NetworkNode> getLocalEntities() {
        Vector<NetworkNode> result = new Vector<NetworkNode>();

        //zoznam novych uzlov, ktore treba este preskumat
        LinkedList<NetworkNode> newNodes = new LinkedList<NetworkNode>();
        
        //zoznam uzlov, ktore uz boli preskumane
        LinkedList<NetworkNode> processedNodes = new LinkedList<NetworkNode>();
        processedNodes.add(this);
        
        for (NetworkLink linka : getLinks()) {
            if (!(linka.getAnotherEnd(this) instanceof GridRouterGlobal)) {
                newNodes.push(linka.getAnotherEnd(this));
            }
        }
        
        while (!newNodes.isEmpty()) {
            NetworkNode uzol = newNodes.removeLast();
            if (!processedNodes.contains(uzol)) {
                processedNodes.add(uzol);
                if (!(uzol instanceof GridRouterGlobal)) {
                    result.add(uzol);
                    for (NetworkLink linka : uzol.getLinks())
                        newNodes.add(linka.getAnotherEnd(uzol));
                }
            }
        }
        
        return result;
    }
    
    /**
     * Vracia meno brokera, na ktoreho je napojeny cez lokalne routre.
     * V spravnej topologii musi vracat null.
     */
    public String getConnectedBroker() {
        Vector<NetworkNode> entities = getLocalEntities();
        for (NetworkNode entity : entities) 
            if (entity instanceof GridBroker)
                return entity.getName();
        return null;
    }
    
    /**
     * Vracia zoznam lokalnych zdrojov pripojenych ku brokerovi.
     */
    public Vector<NetworkNode> getLocalResources() {
        Vector<NetworkNode> entities = getLocalEntities();
        Vector<NetworkNode> result = new Vector<NetworkNode>();
        for(NetworkNode entity : entities)
            if (entity instanceof GridResource)
                result.add(entity);
        
        return result;
    }
    
    public String brokerRouterName() {
        return "_" + getName() + "_extra_router";
    }
    

    public LinkedList<Sim_entity> createGridSimEntities(String globalBrokerName, boolean trace_flag) throws Exception {
        LinkedList<Sim_entity> result = new LinkedList<Sim_entity>();

        RIPRouter extraRouter = new RIPRouter(brokerRouterName(), trace_flag);
        
        SimpleLink spojenie = new SimpleLink(
                        brokerRouterName() + "_link",
                        99999999,
                        1,
                        Integer.MAX_VALUE
                        );
        
        BrokerLocal broker = new BrokerLocal(
                getName(),
                spojenie,
                globalBrokerName
                );
        
        FIFOScheduler linkScheduler = new FIFOScheduler(
                "FIFOScheduler_" + spojenie.get_name());
        
        extraRouter.attachHost(broker, linkScheduler);
        
        result.add(broker);
        result.add(extraRouter);
        
        return result;
    }
    
}
